package cn.devcat.sso.config.security.filter;

import cn.devcat.sso.config.security.SecurityMessageUtil;
import cn.devcat.sso.entity.DevcatApiPattern;
import cn.devcat.sso.entity.DevcatRole;
import cn.devcat.sso.entity.DevcatUser;
import cn.devcat.sso.enums.RsEnum;
import cn.devcat.sso.service.IDevcatApiPatternService;
import cn.devcat.sso.service.IDevcatRoleService;
import cn.devcat.sso.service.IDevcatUserService;
import cn.devcat.sso.util.HttpUtil;
import cn.devcat.sso.util.JWTUtil;
import cn.devcat.sso.util.RedisUtil;
import cn.devcat.sso.util.RsUtil;
import com.auth0.jwt.interfaces.DecodedJWT;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@Component
public class TokenLoginFilter extends OncePerRequestFilter {

	@Autowired
	IDevcatApiPatternService apiPatternService;

	@Autowired
	IDevcatUserService userService;

	@Autowired
	IDevcatRoleService roleService;

	@Autowired
	RedisUtil redisUtil;

	@Override
	protected void doFilterInternal(HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull FilterChain filterChain)
			throws ServletException, IOException {

		AntPathMatcher matcher = new AntPathMatcher();
		String requestUrl = request.getRequestURI().substring(4);
		if (!matcher.match("/login**", requestUrl)) {
			boolean needAuth = false; // 标识是否要检查权限
			List<DevcatApiPattern> apiList = apiPatternService.getAllApiParrentList();
			if (apiList != null && !apiList.isEmpty()) {
				for (DevcatApiPattern api : apiList) {
					if (matcher.match(api.getApiPattern(), requestUrl)) {
						List<DevcatRole> roleListByApi = roleService.getRoleListByApiPatternId(api.getId());
						long count = roleListByApi.stream().map(DevcatRole::getRoleCode)
								.filter(item -> !"ROLE_ANONYMOUS".equals(item)).count();
						if (count > 0) {
							needAuth = true;
							break;
						}else {
							needAuth = false;
						}
						break;
					} else {
						needAuth = true;
					}
				}
			}
			if (needAuth) { // 如果需要校验则进入校验逻辑
				String uuid = request.getHeader(HttpHeaders.AUTHORIZATION);
				if(!redisUtil.checkKey(uuid)) {	//如果redis中没有对应的key说明用户手动退出，或者key已经过期
					HttpUtil.sendResponse(response, RsUtil.error(SecurityMessageUtil.NO_LOGIN, RsEnum.NON_LOGIN));
					return;
				}
				String token = redisUtil.select(uuid);
				if (token == null || token.isEmpty() || "null".equals(token)) {
					HttpUtil.sendResponse(response, RsUtil.error(SecurityMessageUtil.NO_LOGIN, RsEnum.NON_LOGIN));
					return;
				}
				DecodedJWT decode = JWTUtil.decode(token);
				Date expiresAt = decode.getExpiresAt();
				String username = decode.getClaim("username").asString();
				DevcatUser userByUsername = userService.getUserByUsername(username);
				if (userByUsername == null) {
					HttpUtil.sendResponse(response, RsUtil.error(SecurityMessageUtil.NO_ACCESS, RsEnum.NON_AUTHEN));
					return;
				}
				String secret = userByUsername.getPassword();
				boolean checkResult = JWTUtil.checkToken(token, username, secret);
				if (!checkResult) {
					HttpUtil.sendResponse(response, RsUtil.error(SecurityMessageUtil.TOKEN_ERROR, RsEnum.NON_AUTHEN));
					return;
				}
				// 当请求的过期时间剩余1分钟时重新生成token
				long expiresAtTime = expiresAt.getTime();
				long time = new Date().getTime();
				if (expiresAtTime - time < 60 * 1000) {
					String newToken = JWTUtil.sign(username, secret);
					response.setHeader("token", newToken);
				}
				SecurityMessageUtil.checkUserStatus(userByUsername, response);
				List<DevcatRole> roleList = roleService.getRoleListByUserId(userByUsername.getId());
				String roles;
				if (roleList == null || roleList.isEmpty()) {
					roles = "ROLE_USER";
				} else {
					roles = roleList.stream().map(DevcatRole::getRoleCode).collect(Collectors.joining(","));
				}
				userByUsername.setAuthorities(AuthorityUtils.commaSeparatedStringToAuthorityList(roles));
				UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
						userByUsername, null, userByUsername.getAuthorities());
				SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
			}
		}
		filterChain.doFilter(request, response);
	}

}
